<?php
/**
 * Author david you
 * Date 2024/4/3
 * Time 16:22
 */

namespace App\Http\Middleware;

use App\Services\TracerService;
use Closure;
use Exception;
use Laravel\Lumen\Http\Request;
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
use OpenTelemetry\API\Trace\SpanInterface;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\Context\ScopeInterface;
use OpenTelemetry\SemConv\TraceAttributes;
use Throwable;

class TracerMiddleware extends Middleware
{
    /**
     * @var SpanInterface|null
     */
    public ?SpanInterface $span = null;

    /**
     * @var ScopeInterface|null
     */
    private ?ScopeInterface $scope = null;


    /**
     * Handle an incoming request.
     * @param Request $request
     * @param Closure $next
     *
     * @return mixed|void
     * @throws Exception
     */
    public function handle(Request $request, Closure $next)
    {
        try {
            $context = TraceContextPropagator::getInstance()->extract($request->header());
            $this->span = TracerService::instance()->spanBuilder('unknown')
                ->setParent($context)
                ->setSpanKind(isHttp() ? SpanKind::KIND_SERVER : SpanKind::KIND_INTERNAL)
                ->startSpan();
            $this->scope = $this->span->activate();
            $carrier = [];
            TraceContextPropagator::getInstance()->inject($carrier);
            TracerService::$carrier = $carrier;

            return $next($request);
        } catch (Throwable $e) {
            $this->returnError(TRACER_COLLECTION_FAIL, $e->getMessage());
        }
    }


    /**
     * 设置属性
     *
     * @param Request $request
     * @param SpanInterface $span
     * @return SpanInterface
     * @throws Exception
     */
    public function setAttributes(Request $request, SpanInterface $span): SpanInterface
    {
        return isHttp()
            ? $span->setAttributes([
                'component' => 'Http',
                TraceAttributes::HTTP_CLIENT_IP => $request->getClientIp(),
                TraceAttributes::HTTP_METHOD => $_SERVER['REQUEST_METHOD'],
                TraceAttributes::HTTP_URL => $_SERVER['REQUEST_URI'],
                TraceAttributes::HTTP_STATUS_CODE => 200,
                TraceAttributes::PEER_SERVICE => $request->getClientIp(), // 请求客户端
            ])
            : $span->setAttributes([
                'component' => 'RPC',
                TraceAttributes::RPC_SERVICE => $request->route()->getController(),
                TraceAttributes::RPC_METHOD => $request->route()->getActionMethod(),
                TraceAttributes::RPC_GRPC_STATUS_CODE => 0,
                TraceAttributes::PEER_SERVICE => $request->getClientIp(), // 请求客户端
            ]);
    }

    /**
     * 可终止
     *
     * @return void
     * @throws Exception
     */
    public function terminate(Request $request, Closure $next)
    {
        $response = $next($request);
        $this->setAttributes($request, $this->span);
        $this->span->addEvent('request', ['body' => json_encode($request->route()->parameters())]);
        $this->span->addEvent('response', ['body' => $response]);
        $this->span->updateName('/' . $request->route()->getController() . '/' . $request->route()->getActionMethod());
        $this->scope->detach();
        $this->span->end();
        TracerService::forceFlush();
    }

}
